package org.luosl.webmagicx.handler

import org.luosl.webmagicx.Utils.RegexUtils
import org.luosl.webmagicx.conf.{ConfException, Props, SpiderConf}
import us.codecraft.webmagic.{ResultItems, Task}
import org.luosl.webmagicx.conf.MatcherConverters._
import org.luosl.webmagicx.conf.PropType._


class TextHandler(sc:SpiderConf, props:Props)  extends BaseHandler(sc, props) {

  val needProcessCell:List[Props] = props.props("*")

  override def process(items: ResultItems, task:Task): Unit = {
    needProcessCell.foreach{ prop =>
      if(!items.isSkip){
        val processName:String = prop.compName
        val src:String = prop.value("src")(strType)
        val target = prop.value("target")(strType)
        val textOpt:Option[String] = Option(items.get(src))
        val expression:String = prop.value("expression")(strType)
        val takeIndexOpt:Option[Int] = prop.valueOption("takeIndex")(intType)
        val takeContainsOpt:Option[String] = prop.valueOption("takeContains")(strType)
        val processedValue:Any = processName match {
          case "replaceAll" => replaceAll(textOpt, expression, prop.valueOrDefault("replaceAs")(strType)(""))
          case "replace" => replace(textOpt, expression, prop.valueOrDefault("replaceAs")(strType)(""))
          case "replaceFirst" => replaceFirst(textOpt, expression, prop.valueOrDefault("replaceAs")(strType)(""))
          case "split" => split(textOpt, expression, takeIndexOpt, takeContainsOpt)
          case "find" => find(textOpt, expression, takeIndexOpt, takeContainsOpt)
          case str:String =>
            throw ConfException(s"TextHandler 暂不支持:$str 请检查配置文件:${sc.path}")
        }
        val result:String = processedValue match {
          case Some(str:String) => str
          case None =>
            val must:Boolean = prop.valueOrDefault("must")(booleanType)(true)
            if(must) {
              val opDesc:String = s"[name=$processName,src=$src,target=$target,expression=$expression]"
              logInfo(s"TextHandler 执行操作: $opDesc ，未抽取到数据，页面[_url=${items.get("_url")}]将会被跳过其他阶段...")
              items.setSkip(true)
            }
            null
          case str:String => str
        }
        items.put(target, result)
      }
    }
  }

  /**
    * 替换所有
    * @param textOpt text
    * @param expression expression
    * @param replaceAs replaceAs
    * @return
    */
  def replaceAll(textOpt:Option[String],expression:String,replaceAs:String): Option[String] ={
    textOpt.map(_.replaceAll(expression,replaceAs))
  }

  /**
    * 替换
    * @param textOpt text
    * @param expression expression
    * @param replaceAs replaceAs
    * @return
    */
  def replace(textOpt:Option[String],expression:String,replaceAs:String): Option[String] ={
    textOpt.map(_.replace(expression,replaceAs))
  }

  /**
    * 替换第一个
    * @param textOpt text
    * @param expression expression
    * @param replaceAs replaceAs
    * @return
    */
  def replaceFirst(textOpt:Option[String],expression:String,replaceAs:String): Option[String] ={
    textOpt.map(_.replaceFirst(expression,replaceAs))
  }

  /**
    * 拆分
    * @param textOpt text
    * @param expression expression
    * @param takeIndexOpt takeIndexOpt
    * @param takeContainsOpt takeContainsOpt
    * @return
    */
  def split(textOpt:Option[String],expression:String, takeIndexOpt:Option[Int],takeContainsOpt:Option[String]):Option[String] = {
    textOpt.flatMap{ txt=>
      val splitArr:Array[String] = txt.split(expression)
      indexOrContains(splitArr, takeIndexOpt, takeContainsOpt)
    }
  }

  /**
    * 查找
    * @param textOpt text
    * @param expression expression
    * @param takeIndexOpt takeIndexOpt
    * @param takeContainsOpt takeContainsOpt
    * @return
    */
  def find(textOpt:Option[String],expression:String,takeIndexOpt:Option[Int],takeContainsOpt:Option[String]):Option[String] = {
    textOpt.flatMap{ txt=>
      val findArr:Array[String] = RegexUtils.find(txt, expression).toArray
      indexOrContains(findArr, takeIndexOpt, takeContainsOpt)
    }
  }

  /**
    * 根据索引，或包含字符串查找
    * @param arr arr
    * @param takeIndexOpt takeIndexOpt
    * @param takeContainsOpt takeContainsOpt
    * @return
    */
  private def indexOrContains(arr:Array[String],takeIndexOpt:Option[Int],takeContainsOpt:Option[String]):Option[String] = {
    (takeIndexOpt, takeContainsOpt) match {
      case (Some(index), Some(str)) =>
        if(index < arr.length && arr(index).contains(str)) Option(arr(index)) else None
      case (Some(index), None) => if(index < arr.length) Option(arr(index)) else None
      case (None, Some(str)) => arr.find(_.contains(str))
      case (None, None) =>
        throw ConfException(s"split 标签必须配置 takeIndex 或 takeContains 属性,请检查配置文件:${sc.path}")
    }
  }

}
